package com.hzy.es.service;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hzy.es.enums.Code;
import com.hzy.es.result.Response;
import io.searchbox.action.Action;
import io.searchbox.client.JestClient;
import io.searchbox.client.JestResult;
import io.searchbox.client.JestResultHandler;
import io.searchbox.core.*;
import io.searchbox.indices.CreateIndex;
import io.searchbox.indices.DeleteIndex;
import io.searchbox.indices.mapping.GetMapping;
import io.searchbox.indices.mapping.PutMapping;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class ESService {
    @Resource
    private JestClient jestClient;

    @RequestMapping(value = "/createIndex", method = RequestMethod.POST)
    public Object createIndex(String index) throws IOException {
        CreateIndex createIndex = new CreateIndex.Builder(index).build();
        JestResult result = jestClient.execute(createIndex);
        if (result.isSucceeded()) {
            return Response.success(null);
        }
        return Response.fail(Code.ERROR_PARAM.getCode(), result.getErrorMessage());
    }

    @RequestMapping(value = "/deleteIndex", method = RequestMethod.DELETE)
    public Object deleteIndex(String index) throws IOException {
        DeleteIndex deleteIndex = new DeleteIndex.Builder(index).build();
        JestResult result = jestClient.execute(deleteIndex);
        if (result.isSucceeded()) {
            return Response.success(null);
        }
        return Response.fail(Code.ERROR_PARAM.getCode(), result.getErrorMessage());
    }

    @RequestMapping(value = "/putMapping", method = RequestMethod.PUT)
    public Object putMapping(String index, String type, String mapping) throws IOException {
        PutMapping putMapping = new PutMapping.Builder(
                index,
                type,
                mapping
        ).build();

        JestResult result = jestClient.execute(putMapping);
        if (result.isSucceeded()) {
            return Response.success(null);
        }
        return Response.fail(Code.ERROR_PARAM.getCode(), result.getErrorMessage());
    }

    @RequestMapping(value = "/getMapping", method = RequestMethod.GET)
    public Object getMapping(String index) throws IOException {

        Action getMapping = new GetMapping.Builder().addIndex(index).build();
        JestResult result = jestClient.execute(getMapping);

        if (result.isSucceeded()) {
            return Response.success(JSON.parse(result.getJsonString()));
        }
        return Response.fail(Code.ERROR_PARAM.getCode(), result.getErrorMessage());
    }

    /**
     * 异步修改/新增
     * @param indexName
     * @param typeName
     * @param dataList
     * @param id
     * @param jestResultHandler
     * @param <DATA>
     * @return
     */
    public <DATA> Response<Object> insertOrUpdate(String indexName, String typeName, List<DATA> dataList, String id, JestResultHandler<JestResult> jestResultHandler) {
        Bulk.Builder bulk = new Bulk.Builder();
        for (DATA data : dataList) {
            Index index = new Index.Builder(data).
                    index(indexName).
                    type(typeName).
                    id(id).
                    build();
            bulk.addAction(index);
        }
        jestClient.executeAsync(bulk.build(), jestResultHandler);
        return Response.success(null);
    }

    /**
     * 新增/修改
     * @param indexName
     * @param typeName
     * @param dataList
     * @param id
     * @param <DATA>
     * @return
     * @throws Exception
     */
    public <DATA> JestResult insertOrUpdate(String indexName, String typeName, List<DATA> dataList, String id) throws Exception {
        Bulk.Builder bulk = new Bulk.Builder();
        for (DATA data : dataList) {
            Index index = new Index.Builder(data).
                    index(indexName).
                    type(typeName).
                    id(id).
                    build();
            bulk.addAction(index);
        }
        return jestClient.execute(bulk.build());
    }

    /**
     * 修改
     * @param indexName
     * @param typeName
     * @param id
     * @param paramMap
     * @return
     * @throws Exception
     */
    public JestResult update(String indexName, String typeName, String id, Map<String, Object> paramMap) throws Exception {
        Map<String, Object> docMap = Maps.newHashMap();
        docMap.put("doc", paramMap);
        final Update update = new Update.Builder(docMap).id(id).build();
        final Bulk bulk = new Bulk.Builder()
                .addAction(update).defaultIndex(indexName).defaultType(typeName)
                .build();
        return jestClient.execute(bulk);
    }

    // 异步删除
    public Response<Object> deleteByKeyList(String indexName, String typeName, String deleteKeyName, List<String> deleteKeyList, JestResultHandler<JestResult> jestResultHandler) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().filter(QueryBuilders.termsQuery(deleteKeyName, deleteKeyList));
        DeleteByQuery deleteByQuery = new DeleteByQuery.Builder(new SearchSourceBuilder().query(boolQueryBuilder).toString())
                .addIndex(indexName)
                .addType(typeName)
                .refresh(true).build();
        jestClient.executeAsync(deleteByQuery, jestResultHandler);
        return Response.success(null);
    }

    // 同步删除
    public JestResult deleteByKeyList(String indexName, String typeName, String deleteKeyName, List<String> deleteKeyList) throws Exception {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().filter(QueryBuilders.termsQuery(deleteKeyName, deleteKeyList));
        DeleteByQuery deleteByQuery = new DeleteByQuery.Builder(new SearchSourceBuilder().query(boolQueryBuilder).toString())
                .addIndex(indexName)
                .addType(typeName)
                .refresh(true).build();
        return jestClient.execute(deleteByQuery);
    }

    // 按文档ID异步删除
    public Response deleteById(String indexName, String typeName, String id, JestResultHandler<JestResult> jestResultHandler) {
        Delete.Builder builder = new Delete.Builder(id);
        Delete delete = builder.index(indexName).type(typeName).build();
        jestClient.executeAsync(delete, jestResultHandler);
        return Response.success(null);
    }

    /**
     * 查询
     * @param boolQueryBuilder
     * @param indexName
     * @param typeName
     * @param sourceType
     * @param fieldList
     * @param <T>
     * @return
     */
    public <T> Response query(BoolQueryBuilder boolQueryBuilder, String indexName,
                              String typeName, Class<T> sourceType, List<String> fieldList) {
        String query = "";
        try {
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(boolQueryBuilder);
            if (CollectionUtils.isNotEmpty(fieldList)) {
                //指定查询字段，不传则查询全部
                searchSourceBuilder.fetchSource(fieldList.toArray(new String[0]), null);
            }
            query = searchSourceBuilder.toString();

            Search search = new Search.Builder(query).addIndex(indexName).addType(typeName).build();
            SearchResult searchResult = jestClient.execute(search);
            if (!searchResult.isSucceeded()) {
                return Response.fail("500", searchResult.getErrorMessage());
            }
            List<T> resultList = Lists.newArrayList();
            List<SearchResult.Hit<T, Void>> hits = searchResult.getHits(sourceType);
            if (CollectionUtils.isNotEmpty(hits)) {
                for (SearchResult.Hit<T, Void> hit : hits) {
                    resultList.add(hit.source);
                }
            }
            return Response.success(resultList);
        } catch (Exception e) {
            String errorMsg = String.format("从ES查询数据出现异常，indexName=%s, typeName=%s, query=%s, msg=%s", indexName, typeName, query, e.getMessage());
            return Response.fail("500", errorMsg);
        }
    }

    /**
     * 查询数量
     * @param boolQueryBuilder
     * @param indexName
     * @param typeName
     * @return
     */
    public Response<Double> queryCount(BoolQueryBuilder boolQueryBuilder, String indexName, String typeName) {
        String query = "";
        try {
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(boolQueryBuilder);
            query = searchSourceBuilder.toString();
            Count count = new Count.Builder()
                    .addIndex(indexName)
                    .addType(typeName)
                    .query(query)
                    .build();

            CountResult countResult = jestClient.execute(count);
            if (!countResult.isSucceeded()) {
                return Response.fail("500", countResult.getErrorMessage());
            }
            Double resultsCount = countResult.getCount();
            return Response.success(resultsCount.longValue());
        } catch (Exception e) {
            String errorMsg = String.format("从ES查询数量出现异常，indexName=%s, typeName=%s, query=%s, msg=%s", indexName, typeName, query, e.getMessage());
            log.error(errorMsg, e);
            return Response.fail("500", errorMsg);
        }
    }
}
